home *** CD-ROM | disk | FTP | other *** search
/ Carousel / CAROUSEL.cdr / mactosh / utilprn / hpdeskje.sit / HPDJet ƒ / PDEF5.c < prev    next >
C/C++ Source or Header  |  1989-04-02  |  21KB  |  705 lines

  1. /* 02.04.1989  amn  (latest edit) */
  2.  
  3. /* PDEF5.c  -  printer driver for Macintosh and HP DeskJet, spool file printing. */
  4.  
  5. /* Compiles into 'PDEF' resource, id 5, name ''. */
  6. /* We cannot use any global variables in this code.  However, the globals of the */
  7. /* low-level driver (XPrint) are available thru the handle 'dCtlStorage' in the */
  8. /* driver's device control entry. */
  9.  
  10. /* This resource is placed into the printer resource file */
  11. /* 'HP DeskJet', type 'PRER', creator 's89^' as */
  12. /* 'PDEF' 5 '' by 'PRER_Builder' utility program.  This utility adds a small jump */
  13. /* table in front of the code produced by LightspeedC. */
  14.  
  15. /* This procedure handles spool file printing... */
  16.  
  17. /* Authors:  Ari Mujunen (amn@hutcs.hut.fi) and Olli Arnberg (oar@hutcs.hut.fi). */
  18. /* Copyright Ari Mujunen, Olli Arnberg 1989. */
  19. /* You may redistribute the driver (=printer resource file, source files, */
  20. /* documentation file(s), and the file 'Copyright and Source Offer') */
  21. /* only _non-commercially_ and _in its entirety_. */
  22. /* See the file 'Copyright and Source Offer' and/or documentation for details. */
  23. /* Acknowledgements:  Special thanks to Mr. Earle R. Horton for his 'Daisy' */
  24. /* daisywheel printer driver and its source code published in 'MacTutor', Nov-Dec 1987. */
  25. /* This driver served as a basis and inspiration for our work.  It also */
  26. /* proofed that a Macintosh printer driver can be done despite the lack of */
  27. /* documentation from Apple. */
  28.  
  29. /* Change history: */
  30. /* Version  When        Who      Why */
  31. /* 2.0      09.12.1988  amn      Original rewrite. */
  32. /*          11.12.1988  amn      Cleaning up. */
  33. /*          12.12.1988  amn      HP DeskJet's physical margins taken into account. */
  34. /*          15.12.1988  amn      Deletion of print file.  Banding buffer cleared */
  35. /*                               before each band. */
  36. /*          07.03.1989  amn,oar  Copies-loop. */
  37. /*          10.03.1989  amn      Re-microspacing of text by spExtra. */
  38. /*          11.03.1989  amn      Re-microspacing of text char by char. */
  39. /*          24.03.1989  amn      Long strings (>= 2040 pixels) are chopped. */
  40. /*          26.03.1989  amn      GrafPort is preserved across idle proc call. */
  41. /*          26.03.1989  amn      Settable printer origin. */
  42. /*          31.03.1989  amn      If we are making a permanent spool file, */
  43. /*                               do not print anything. */
  44. /*                               Updating TPrStatus output parameter. */
  45. /* 2.1      02.04.1989  amn,oar  Released version. */
  46.  
  47.  
  48. #include "common_mac_includes.h"
  49.  
  50. /* Mac OS includes specific to this module: */
  51. /* None yet. */
  52.  
  53.  
  54. #include "prglobals.h"
  55.  
  56. #include "procedures_for_PDEF5.c"
  57.  
  58.  
  59. #define BANDBITMAPBUFFERSIZE 30000
  60.  
  61.  
  62.  
  63. /* Function prototypes. */
  64.  
  65. /* Procedure dispatcher. */
  66. void main(int);
  67.  
  68. /* Opens and prints spool file. */
  69. pascal void myPrPicFile(THPrint, TPPrPort, Ptr, Ptr, TPrStatus *);
  70.  
  71. /* Prints the whole spool file by calling 'drawOnePage' for pages found in spool file. */
  72. void drawSpoolFile(int, TPPrPort, Ptr, TPrStatus *);
  73.  
  74. /* Repeatedly draws the picture of a given page to a partial bit map called 'band'. */
  75. /* Once a band is drawn, sends it to the printer using XPrint. */
  76. void drawOnePage(int, long, TPPrPort, Ptr, TPrStatus *);
  77.  
  78.  
  79. /* Replaces the standard QuickDraw low-level procedure for drawing text. */
  80. pascal void myStdText(int, Ptr, Point, Point);
  81.  
  82. /* Draws string in parts, the length of which is less than 2000 pixels. */
  83. pascal void stdTextInParts(int, int, Ptr, Point, Point, Ptr);
  84.  
  85. /* Used to call standard QD text-drawing/measuring procedure.  We use prototypes... */
  86. pascal void CallPascal(int, Ptr, Point, Point, Ptr);
  87.  
  88. /* Processes "string original length" picture comments. */
  89. pascal void myStdComment(int, int, htApplicationComment);
  90.  
  91. /* Gets picture data for QuickDraw directly from spool file. */
  92. pascal void getPICTData(Ptr, int);
  93.  
  94.  
  95. /* Function definitions. */
  96.  
  97.  
  98. void
  99. main(routineSelector)
  100. int routineSelector;
  101. {
  102.     /* The jump table code inserted before our code resource by 'PRER_Builder' */
  103.     /* utility program pushes a word onto stack indicating which routine is called. */
  104.     /* We pop it off the stack and select an appropriate routine. */
  105.     
  106.     switch(routineSelector) {
  107.         case 0:
  108.             asm {
  109.                 unlk a6  ;; LSC generates an 'link' instruction to access parameters
  110.                 move.l (a7)+, d0  ;; pop return address to our jump table code (discarded)
  111.                 move.w (a7)+, d0  ;; pop argument 'routineSelector'
  112.                 jmp myPrPicFile
  113.             }
  114.     }  /* switch */
  115.     
  116.     /* We should not arrive here; Printing Manager has called a non-existent routine. */
  117.     SysError(5);  /* check bounds trap ??? */
  118. }  /* main */
  119.  
  120.  
  121. pascal void
  122. myPrPicFile(hPrint, pPrPort, pIOBuf, pDevBuf, prStatus)
  123. THPrint        hPrint;
  124. TPPrPort    pPrPort;
  125. Ptr            pIOBuf, pDevBuf;
  126. TPrStatus    *prStatus;
  127. {
  128.     TPPrPort thisPort;
  129.     Ptr bandBuffer;
  130.     ptXprintGlobals xPrintGlobals;
  131.     OSErr retCode;
  132.  
  133.     {  /* Lock this code resource in memory (Printing Manager should do this, but...). */
  134.         Handle    us;
  135.         
  136.         us = (Handle)GetResource('PDEF', 5);
  137.         if (us == nil) {
  138.             PrintErr = ResError();
  139.             return;
  140.         }
  141.         HLock(us);
  142.     }
  143.     
  144.     xPrintGlobals = GET_XPRINT_GLOBALS;
  145.     
  146.     /* If we are making a permanent spool file, do not print now. */
  147.     if (xPrintGlobals->spoolFileIsNamedAndPermanent)
  148.         return;
  149.     
  150.     if (pPrPort == nil) {  /* printing GrafPort NOT preallocated */
  151.         if ((thisPort = (TPPrPort)NewPtr((long)sizeof(TPrPort))) == nil) {
  152.             PrintErr = iMemFullErr;
  153.             return;
  154.         }
  155.         thisPort->fOurPtr = TRUE;
  156.     }
  157.     else {  /* printing GrafPort already preallocated */
  158.         thisPort = pPrPort;
  159.         thisPort->fOurPtr = FALSE;
  160.     }
  161.     prStatus->pPrPort = thisPort;  /* let the application see the pointer to printing port, too */
  162.     prStatus->hPrint = hPrint;  /* why on earth we should return the handle application gave us? */
  163.     prStatus->fPgDirty = TRUE;  /* always... ??? */
  164.     prStatus->fImaging = TRUE;
  165.     
  166.     OpenPort(thisPort);  /* this could cause memory manager error ??? */
  167.     
  168.     if ((bandBuffer = NewPtr((long)(BANDBITMAPBUFFERSIZE))) == nil) {
  169.         retCode = iMemFullErr;
  170.         goto cleanUp;
  171.     }
  172.     
  173.     if (retCode = FSOpen(
  174.             xPrintGlobals->printRecord.prJob.pFileName,
  175.             xPrintGlobals->printRecord.prJob.iFileVol,
  176.             &xPrintGlobals->spoolFileRefNum) != noErr)  /* getPICTData needs this global refnum */
  177.         goto disposAndCleanUp;
  178.     
  179.     /* If we had our own idle proc, it is now in different address. */
  180.     if (!xPrintGlobals->applicationOwnsIdleProc)
  181.         xPrintGlobals->printRecord.prJob.pIdleProc = (ProcPtr)checkForCommandPeriod;
  182.     
  183.     prStatus->iTotCopies = xPrintGlobals->printRecord.prJob.iCopies;
  184.     for (prStatus->iCurCopy=0;
  185.         prStatus->iCurCopy < prStatus->iTotCopies;
  186.         prStatus->iCurCopy++) {
  187.         drawSpoolFile(xPrintGlobals->spoolFileRefNum, thisPort, bandBuffer, prStatus);
  188.     }
  189.     
  190.     (void)FSClose(xPrintGlobals->spoolFileRefNum);
  191.     (void)FSDelete(
  192.         xPrintGlobals->printRecord.prJob.pFileName,
  193.         xPrintGlobals->printRecord.prJob.iFileVol
  194.     );
  195.     DisposPtr(bandBuffer);
  196.     
  197.     retCode = noErr;
  198. cleanUp:
  199.     PrintErr = retCode;
  200.     ClosePort(thisPort);
  201.     if (thisPort->fOurPtr)
  202.         DisposPtr(thisPort);
  203.     return;
  204.  
  205. disposAndCleanUp:
  206.     DisposPtr(bandBuffer);
  207.     goto cleanUp;
  208. }  /* myPrPicFile */
  209.  
  210.  
  211. void
  212. drawSpoolFile(spoolFileRefNum, thisPort, bandBuffer, prStatus)
  213. int spoolFileRefNum;
  214. TPPrPort    thisPort;
  215. Ptr            bandBuffer;
  216. TPrStatus    *prStatus;
  217. {
  218.     THPfHeader    hHeader;
  219.     long        count;
  220.     int            i;
  221.     OSErr        retCode;
  222.  
  223.     hHeader = (THPfHeader)NewHandle((long)sizeof(TPfHeader));
  224.     if (hHeader == nil)
  225.         return;
  226.     
  227.     retCode = SetFPos(spoolFileRefNum, fsFromStart, 0L);
  228.     if (retCode != noErr) {
  229.         DisposHandle(hHeader);
  230.         return;
  231.     }
  232.     
  233.     count = sizeof(TPfHeader);
  234.     HLock(hHeader);
  235.     retCode = FSRead(spoolFileRefNum, &count, *hHeader);
  236.     HUnlock(hHeader);
  237.     if (retCode != noErr) {
  238.         DisposHandle(hHeader);
  239.         return;
  240.     }
  241.     
  242.     prStatus->iTotPages = iPfMaxPgs;  /* hmm, _max_ (=128) ??? */
  243.     for (i=1; i<=prStatus->iTotPages; i++)
  244.         if ( (*hHeader)->pfPgDir.lPgPos[i] > 0 ) {
  245.             prStatus->iCurPage = i;
  246.             drawOnePage(
  247.                 spoolFileRefNum,
  248.                 (*hHeader)->pfPgDir.lPgPos[i],
  249.                 thisPort,
  250.                 bandBuffer,
  251.                 prStatus
  252.             );
  253.             if (PrintErr != noErr)
  254.                 goto cleanUp;
  255.         }
  256.         
  257. cleanUp:
  258.     DisposHandle(hHeader);
  259. }  /* drawSpoolFile */
  260.  
  261.  
  262. void
  263. drawOnePage(spoolFileRefNum, offset, thisPort, bandBuffer, prStatus)
  264. int        spoolFileRefNum;
  265. long    offset;
  266. TPPrPort    thisPort;
  267. Ptr            bandBuffer;
  268. TPrStatus    *prStatus;
  269. {
  270.     ptXprintGlobals xPrintGlobals;
  271.     ptPrParam pXPrintParameterBlock;
  272.     OSErr retCode;
  273.     Rect        pageRect,
  274.                 bigRect;
  275.     QDProcs        qdp;
  276.     int            resolution;
  277.     int            bandHeight, bandRowBytes, i;
  278.     Point        marginOffset;
  279.  
  280.     xPrintGlobals = GET_XPRINT_GLOBALS;
  281.     pXPrintParameterBlock = &xPrintGlobals->xPrintParameterBlock;
  282.     
  283.     if ((xPrintGlobals->printRecord.prStl.feed == feedCut)
  284.         && (waitNextPage() == FALSE)) { /* Dialog 'Insert next sheet...' */
  285.         PrintErr = iPrAbort;
  286.         return;
  287.     }
  288.                 
  289.     if ((prStatus->hPic = (PicHandle)NewHandle((long)sizeof(Picture))) == nil) {
  290.         PrintErr = iMemFullErr;
  291.         return;
  292.     }
  293.     
  294.     {  /* Read picture header (page rectangle (=drawing area) is in the picture frame. */
  295.         long    count;
  296.         
  297.         if ((retCode = SetFPos(spoolFileRefNum, fsFromStart, offset)) != noErr)
  298.             goto cleanUp;
  299.         
  300.         count = sizeof(Picture);
  301.         HLock(prStatus->hPic);
  302.         retCode = FSRead(spoolFileRefNum, &count, *(prStatus->hPic));
  303.         HUnlock(prStatus->hPic);
  304.         if (retCode != noErr)
  305.             goto cleanUp;
  306.     }
  307.     
  308.     /* This is the drawing area application drawed into: */
  309.     /* pageRect = (**(prStatus->hPic)).picFrame; */
  310.     /* ...but we are going to use page rectangle, since application might have wanted */
  311.     /* to scale its drawing into the page rectangle. */
  312.     pageRect = xPrintGlobals->printRecord.prInfo.rPage;
  313.     
  314.     { /* Calculate target bitmap dimensions. */
  315.         Rect    tr1, tr2;
  316.         
  317.         tr1.top = 0;
  318.         tr1.left = 0;
  319.         tr1.bottom = 100;
  320.         tr1.right = 100;
  321.         tr2.top = 0;
  322.         tr2.left = 0;
  323.         tr2.bottom = tr2.right = 4*xPrintGlobals->printRecord.printX[7];
  324.         resolution = (xPrintGlobals->printRecord.prStl.wDev & RES_MASK) + 1;  /* should be 1=300, 2=150, 3=100, 4=75 */
  325.         tr2.right /= resolution;
  326.         tr2.bottom /= resolution;
  327.         /* if "Exact dimensions" are required, we should multiply by 1.04 (=75/72) */
  328.         
  329.         bigRect = pageRect;
  330.         MapRect(&bigRect, &tr1, &tr2);  /* This is the (virtual) bitmap area we are going to play the recorded picture. */
  331.     }
  332.     
  333.     /* Calculate the vertical difference between DeskJet's physical left margin (1/8") */
  334.     /* and requested left margin (-rPaper.left) in printer pixels. */
  335.     marginOffset.v = -(xPrintGlobals->printRecord.rPaper.left
  336.         + xPrintGlobals->currentSettings.printerOrigin.h) * 4 / resolution;
  337.     marginOffset.h = -(xPrintGlobals->printRecord.rPaper.top
  338.         + xPrintGlobals->currentSettings.printerOrigin.v) * 4 / resolution;
  339.     
  340.     /* Calculate the bandHeight from resolution and available memory for bit map. */
  341.     bandRowBytes = ((bigRect.right - bigRect.left + marginOffset.v) / 16 + 1) * 2;  /* QD wants even number of bytes */
  342.     bandHeight =  BANDBITMAPBUFFERSIZE / bandRowBytes;
  343.     
  344.     
  345.     InitPort(thisPort);  /* current and fresh */
  346.     
  347.     /* GrafDevice(?) ??? */
  348.     
  349.     {  /* Set the printing grafport to draw into offscreen bit image 'bandBuffer'. */
  350.         BitMap bm;
  351.     
  352.         bm.baseAddr = bandBuffer;
  353.         bm.rowBytes = bandRowBytes;
  354.         bm.bounds.top = 0;
  355.         bm.bounds.left = 0;
  356.         bm.bounds.bottom = bandHeight;
  357.         bm.bounds.right = bandRowBytes*8;
  358.         SetPortBits(&bm);
  359.     }
  360.     
  361.     /* Set the port rectangle to the virtual bitmap rectangle (=enlarged page). */
  362.     thisPort->gPort.portRect = bigRect;
  363.     
  364.     /* visRgn should be what ??? */
  365.     RectRgn(
  366.         thisPort->gPort.visRgn,
  367.         &thisPort->gPort.portRect
  368.     );
  369.     
  370.     SetStdProcs(&qdp);
  371.     qdp.textProc = (QDPtr)myStdText;
  372.     qdp.commentProc = (QDPtr)myStdComment;
  373.     qdp.getPicProc = (QDPtr)getPICTData;
  374.     thisPort->gPort.grafProcs = (QDProcsPtr) &qdp;  /* actually we have the procs in the PRINTING GrafPort */
  375.     
  376.     /* Calculate total number of bands. */
  377.     prStatus->iTotBands = (bigRect.bottom - bigRect.top) / bandHeight;
  378.     for (i=0; i <= prStatus->iTotBands; i++) {
  379.         Rect    tempClip;
  380.         long    count;
  381.         
  382.         prStatus->iCurBand = i;
  383.         
  384.         MovePortTo(marginOffset.v, -(bandHeight * i) + marginOffset.h);
  385.         
  386.         /* Because XPrint currently cannot limit printing to BitMap's 'bounds' rectangle, */
  387.         /* we clear it to prevent unused margin bits which are garbage. */
  388.         /* Note that we cannot use 'EraseRect()' because it draws only within port's */
  389.         /* clip&visRgns and portRect and portBit.bounds. */
  390.         {
  391.             Ptr p;
  392.             
  393.             for(p=bandBuffer+BANDBITMAPBUFFERSIZE-1; p>=bandBuffer; p--)
  394.                 *p = (unsigned char)0;
  395.         }
  396.         
  397. /***        tempClip.top = bandHeight * i - marginOffset.h;
  398.         tempClip.left = bigRect.left;
  399.         if (i==prStatus->iTotBands)
  400.             tempClip.bottom = tempClip.top + (bigRect.bottom - bigRect.top) % bandHeight;
  401.         else
  402.             tempClip.bottom = tempClip.top + bandHeight;
  403.         tempClip.bottom -= marginOffset.h;
  404.         tempClip.right = bigRect.left + bandRowBytes*8;  / * actually bigRect.right is ok ??? * /
  405.         ClipRect(&tempClip);
  406.         EraseRect(&tempClip); ***/
  407.         
  408.         count = offset + (long)sizeof(Picture);
  409.         if ((retCode = SetFPos(spoolFileRefNum, fsFromStart, count)) != noErr)
  410.             goto cleanUp;
  411.         DrawPicture(prStatus->hPic, &bigRect);
  412.         
  413.         /* Run the background procedure before every band. */
  414.         {
  415.             GrafPtr savePort;
  416.  
  417.             GetPort(&savePort);
  418.             (*(xPrintGlobals->printRecord.prJob.pIdleProc))();
  419.             SetPort(savePort);
  420.         }
  421.         if (PrintErr == iPrAbort) {
  422.             retCode = PrintErr;
  423.             goto cleanUp;
  424.         }
  425.  
  426.         /* Send band bit map to low-level driver. */
  427.         pXPrintParameterBlock->csCode = iPrBitsCtl;
  428.         pXPrintParameterBlock->lParam1 = (long)(&(thisPort->gPort.portBits));
  429.         if (i==prStatus->iTotBands)
  430.             thisPort->gPort.portBits.bounds.bottom =
  431.                 thisPort->gPort.portBits.bounds.top
  432.                     + (bigRect.bottom - bigRect.top) % bandHeight;
  433.         pXPrintParameterBlock->lParam2 = (long)(&(thisPort->gPort.portBits.bounds));
  434.         pXPrintParameterBlock->lParam3 = (long)(300 / resolution);
  435.         if ((retCode = PBControl(pXPrintParameterBlock, FALSE)) != noErr)
  436.             goto cleanUp;
  437.             
  438.         /* We cannot accept command-. -interrupt during bit map printing. */
  439.     
  440.     }
  441.     /* Form feed: */
  442.     pXPrintParameterBlock->csCode = iPrDevCtl;
  443.     pXPrintParameterBlock->lParam1 = lPrPageEnd;
  444.     if ((retCode = PBControl(pXPrintParameterBlock, FALSE)) != noErr)
  445.         goto cleanUp;
  446.     
  447.     retCode = noErr;
  448. cleanUp:
  449.     PrintErr = retCode;
  450.     thisPort->gPort.grafProcs = nil;
  451.     DisposHandle(prStatus->hPic);
  452.     return;
  453. }  /* drawOnePage */
  454.  
  455.  
  456. pascal void
  457. myStdText(byteCount, textBuffer, numer, denom)
  458. int byteCount;
  459. Ptr textBuffer;
  460. Point numer, denom;
  461. {
  462.     ptXprintGlobals xPrintGlobals;
  463.     QDProcs stdProcs;
  464.     
  465.     int lengthByQuickDraw;
  466.     Fixed intermediateLength;
  467.     int lengthByQuickDrawAfterScaling;
  468.     Point measuringNumer;
  469.     Point measuringDenom;
  470.     FontInfo measuringInsistOnReturningThis;
  471.     int lengthDifference;
  472.     
  473.     int i;
  474.     int numberOfBlanks;
  475.     
  476.     xPrintGlobals = GET_XPRINT_GLOBALS;
  477.     
  478.     SetStdProcs(&stdProcs);
  479.     
  480.     /* Call standard text measurement routine. */
  481.     /* Would use CallPascal, but this is a function and returns int. */
  482.     measuringNumer = numer;
  483.     measuringDenom = denom;
  484.     asm {
  485.         subq.l    #2, a7  ;; Room for function return
  486.         move.w    byteCount, -(a7)
  487.         move.l    textBuffer, -(a7)
  488.         pea        measuringNumer
  489.         pea        measuringDenom
  490.         pea        measuringInsistOnReturningThis
  491.         _StdTxMeas
  492.         move.w    (a7)+, lengthByQuickDraw
  493.     }
  494.     
  495.     lengthDifference =
  496.         FixRound(
  497.             FixMul(
  498.                 FixRatio(xPrintGlobals->originalStringLength, 1),
  499.                 FixRatio(numer.h, denom.h)
  500.             ) -
  501.             (intermediateLength = FixMul(
  502.                 FixRatio(lengthByQuickDraw, 1),
  503.                 FixRatio(measuringNumer.h, measuringDenom.h)
  504.             ))
  505.         );
  506.     lengthByQuickDrawAfterScaling = FixRound(intermediateLength);
  507.  
  508.     /* If difference is insignificant, draw text using standard procedure. */
  509.     if ((lengthDifference >= -1) && (lengthDifference <= 1)) {
  510.         stdTextInParts(
  511.             lengthByQuickDrawAfterScaling,
  512.             byteCount,
  513.             textBuffer, numer, denom, stdProcs.textProc
  514.         );
  515.         return;
  516.     }
  517.     
  518.     /* Count blanks and adjust spExtra if space characters do not */
  519.     /* shrink to less than half or widen to more than double width. */
  520.     numberOfBlanks = 0;
  521.     for (i=0;i<byteCount;i++)
  522.         if (textBuffer[i]==' ')
  523.             numberOfBlanks++;
  524.     if (numberOfBlanks > 0) {  /* if there are spaces to be exploited... */
  525.         unsigned char blank;
  526.         int lengthOfSpaceCharacterInPixels;
  527.         int absoluteValueOfLengthDifferenceToleratedBySpacesInTheString;
  528.         
  529.         /* Measure the width of a space character. */
  530.         /* Would use CallPascal, but this is a function and returns int. */
  531.         blank = ' ';
  532.         measuringNumer = numer;
  533.         measuringDenom = denom;
  534.         asm {
  535.             subq.l    #2, a7  ;; Room for function return
  536.             move.w    #1, -(a7)
  537.             pea        blank
  538.             pea        measuringNumer
  539.             pea        measuringDenom
  540.             pea        measuringInsistOnReturningThis
  541.             _StdTxMeas
  542.             move.w    (a7)+, lengthOfSpaceCharacterInPixels
  543.         }
  544.         
  545.         absoluteValueOfLengthDifferenceToleratedBySpacesInTheString =
  546.             /*  lengthOfSpaceCharacterInPixels * numberOfBlanks / 2 */
  547.             FixRound(
  548.                 FixMul(
  549.                     FixRatio(lengthOfSpaceCharacterInPixels * numberOfBlanks, 2),
  550.                     FixRatio(measuringNumer.h, measuringDenom.h)
  551.                 )
  552.             );
  553.         
  554.         /* If spaces can absorb the length difference, we use them. */
  555.         if ((lengthDifference >= - absoluteValueOfLengthDifferenceToleratedBySpacesInTheString)
  556.             && (lengthDifference <= absoluteValueOfLengthDifferenceToleratedBySpacesInTheString)) {
  557.             GrafPtr gp;
  558.             Fixed savedSpExtra;
  559.             
  560.             GetPort(&gp);
  561.             savedSpExtra = gp->spExtra;
  562.             
  563.             SpaceExtra(savedSpExtra +
  564.                 FixMul(
  565.                     FixRatio(lengthDifference, numberOfBlanks),
  566.                     FixRatio(denom.h, numer.h)
  567.                 )
  568.             );
  569.             stdTextInParts(
  570.                 lengthByQuickDrawAfterScaling,
  571.                 byteCount,
  572.                 textBuffer, numer, denom, stdProcs.textProc
  573.             );
  574.             
  575.             SpaceExtra(savedSpExtra);
  576.             return;
  577.         }  /* if spaces are used */
  578.     }  /* if there was at least one space */
  579.     
  580.     /* Otherwise we must used the brute force method: draw char by char. */
  581.     /* First we recalculate the length of the string in pixels */
  582.     /* _character by character_.  This is because we are going to draw the string */
  583.     /* in that way and round-off errors will occur while QD positions the pen. */
  584.  
  585.     lengthByQuickDraw = 0;
  586.     for (i=0;i<byteCount;i++) {
  587.         Ptr p;
  588.         int length;
  589.         
  590.         p = &(textBuffer[i]);
  591.         measuringNumer = numer;
  592.         measuringDenom = denom;
  593.         asm {
  594.             subq.l    #2, a7  ;; Room for function return
  595.             move.w    #1, -(a7)
  596.             move.l    p, -(a7)
  597.             pea        measuringNumer
  598.             pea        measuringDenom
  599.             pea        measuringInsistOnReturningThis
  600.             _StdTxMeas
  601.             move.w    (a7)+, length  ;; here it rounds off
  602.         }
  603.         lengthByQuickDraw += length;
  604.     }  /* for */
  605.     
  606.     lengthDifference =
  607.         FixRound(
  608.             FixMul(
  609.                 FixRatio(xPrintGlobals->originalStringLength, 1),
  610.                 FixRatio(numer.h, denom.h)
  611.             ) -
  612.             (intermediateLength = FixMul(
  613.                 FixRatio(lengthByQuickDraw, 1),
  614.                 FixRatio(measuringNumer.h, measuringDenom.h)
  615.             ))
  616.         );
  617.     lengthByQuickDrawAfterScaling = FixRound(intermediateLength);
  618.  
  619.     {
  620.         int numberOfInterCharacterSlots;
  621.         int cumulative;
  622.         int pixelsToOffsetForThisCharacter;
  623.         
  624.         numberOfInterCharacterSlots = byteCount - 1;
  625.         cumulative = 0;
  626.         for (i=0;i<numberOfInterCharacterSlots;i++) {
  627.             CallPascal(1, &(textBuffer[i]), numer, denom, stdProcs.textProc);  /* draw one char */
  628.             cumulative += lengthDifference;
  629.             if ((pixelsToOffsetForThisCharacter = cumulative / numberOfInterCharacterSlots) != 0) {
  630.                 Move(pixelsToOffsetForThisCharacter, 0);
  631.                 cumulative -= (pixelsToOffsetForThisCharacter * numberOfInterCharacterSlots);
  632.             };  /* if we are moving pen by ourselves now */
  633.         }  /* for */
  634.         CallPascal(1, &(textBuffer[numberOfInterCharacterSlots]), numer, denom, stdProcs.textProc);  /* draw one char */
  635.     }
  636. }  /* myStdText */
  637.  
  638.  
  639. pascal void
  640. stdTextInParts(lengthByQuickDrawAfterScaling, byteCount, textBuffer, numer, denom, stdTextProcPtr)
  641. int lengthByQuickDrawAfterScaling;
  642. int byteCount;
  643. Ptr textBuffer;
  644. Point numer, denom;
  645. Ptr stdTextProcPtr;
  646. {
  647.     int numberOfStringParts;
  648.     int charactersInAPart;
  649.     int remainingCharacters;
  650.     int i;
  651.     
  652.     /* There is a bug/limitation in QD: the intermediate bitmap */
  653.     /* into which text is drawn cannot be wider than 2040 pixels */
  654.     /* (rowButes <= 255).  This intermediate bitmap is probably */
  655.     /* on stack -- it is not needed for unscaled text without stylistic variations. */
  656.  
  657.     numberOfStringParts = (lengthByQuickDrawAfterScaling/2000) + 1;
  658.     charactersInAPart = byteCount / numberOfStringParts;
  659.     remainingCharacters = byteCount % numberOfStringParts;
  660.     
  661.     for (i=1;i<=numberOfStringParts;i++) {
  662.         CallPascal(
  663.             (i==numberOfStringParts) ? charactersInAPart+remainingCharacters : charactersInAPart,
  664.             textBuffer, numer, denom, stdTextProcPtr);
  665.         textBuffer += charactersInAPart;
  666.     }  /* for */
  667. }  /* stdTextInParts */
  668.  
  669.  
  670. pascal void
  671. myStdComment(kind, dataSize, dataHandle)
  672. int kind;
  673. int dataSize;
  674. htApplicationComment dataHandle;
  675. {
  676.     ptXprintGlobals xPrintGlobals;
  677.  
  678.     if ((kind != 100) || (dataSize != sizeof(tApplicationComment)))
  679.         return;
  680.     if ((*dataHandle)->applicationSignature != 's89^')
  681.         return;
  682.     if ((*dataHandle)->localKind != 0)
  683.         return;
  684.     
  685.     /* Now it must be our comment, let's save the original length of next string: */
  686.     xPrintGlobals = GET_XPRINT_GLOBALS;
  687.     xPrintGlobals->originalStringLength =
  688.         (*dataHandle)->originalLengthOfNextTextInPixels;
  689. }  /* myStdComment */
  690.  
  691.  
  692. pascal void
  693. getPICTData(bufferPtr, bytesRequested)
  694. Ptr    bufferPtr;
  695. int    bytesRequested;
  696. {
  697.     long    count;
  698.     ptXprintGlobals xPrintGlobals;
  699.  
  700.     xPrintGlobals = GET_XPRINT_GLOBALS;
  701.     count = bytesRequested;
  702.     /*** retCode = ***/ FSRead(xPrintGlobals->spoolFileRefNum, &count, bufferPtr);
  703.     /* if (retCode != noErr) fill buffer with 0xFF, or, end of PICT data */
  704. }  /* getPICTData */
  705.